import logging
from error import ConnectionStateError, InactiveCloseError

log = logging



class Transport:
    def connect(self):
        raise NotImplementedError()

    def disconnect(self):
        raise NotImplementedError()

    def is_connected(self):
        raise NotImplementedError()

    def send(self, byte_buffer):
        raise NotImplementedError()

    def recv(self, buf_size):
        raise NotImplementedError()

    def close(self):
        raise NotImplementedError()

    def handle(self):
        raise NotImplementedError()

    @staticmethod
    def from_tcp(port=12345, ip="127.0.0.1"):
        return TcpNonBlockTransport(ip, port)




from tcp_socket import NonBlockTcpSocket
class TcpNonBlockTransport(Transport):
    def __init__(self, ip, port):
        self.__tcp = NonBlockTcpSocket().remote(ip, port)
        self.__is_connected = False

    #not set __is_connected flags, because of nonblock transport
    def connect(self):
        if not self.is_connected():
            return self.__tcp.connect()
        else:
            log.error("Connected before [{}] connecting to remote".format(str(self)))
            raise ConnectionStateError()

    def disconnect(self):
        if self.is_connected():
            self.__tcp.disconnect()
            self.set_is_connected(False)
            log.info("Disconnected:{}".format(str(self)))

    def is_connected(self):
        return self.__is_connected
    def set_is_connected(self, connected):
        self.__is_connected = connected
        return self 

    #TODO: send with inactive-close socket , to be tested
    def send(self, byte_buffer):
        if self.is_connected():
            if byte_buffer is not None and len(byte_buffer) > 0:
                return self.__tcp.write(byte_buffer)
            else:
                return 0;
        else: 
            log.error("Not connected before Send: {}".format(str(self)))
            raise ConnectionStateError(self.is_connected())

    def recv(self, buf_size):
        if self.is_connected():
            data = self.__tcp.read(buf_size)
            if len(data) == 0: # peer is disconnected
                self.__is_connected = False
                log.info("[{}] got remote closed".format(str(self)))
                raise InactiveCloseError("remote closed when recv")
            return data
        else:
            log.error("Not connected before Recv: {}".format(str(self)))
            raise ConnectionStateError(self.is_connected())

    def close(self):
        self.disconnect()
        return self.__tcp.close()

    def handle(self):
        return self.__tcp.handle()

    def __str__(self):
        return "TcpNonBlockTransport:{}, connect state:{}".format(self.__tcp, self.is_connected())

import unittest 
class TestTransport(unittest.TestCase):
    def test_tcp_transport(self):
        trans = Transport.from_tcp()
        trans.close()


if __name__ == "__main__":
    logging.basicConfig(level=logging.DEBUG)
    unittest.main(verbosity=2)
